---
sidebar_position: 0
description: Define a mapper with Mapperly.
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Mapper configuration

The `MapperAttribute` provides options to customize the generated mapper class.

## Default Mapper configuration

The `MapperDefaultsAttribute` allows to set default configurations applied to all mappers on the assembly level.

```csharp
[assembly: MapperDefaults(EnumMappingIgnoreCase = true)]
```

## Copy behavior

By default, Mapperly does not create deep copies of objects to improve performance.
If an object can be directly assigned to the target, it will do so
(eg. if the source and target type are both `Car[]`, the array and its entries will not be cloned).
To create deep copies, set the `UseDeepCloning` property on the `MapperAttribute` to `true`.

```csharp
// highlight-start
[Mapper(UseDeepCloning = true)]
// highlight-end
public partial class CarMapper
{
  ...
}
```

:::note
Deep cloning is not applied to `IQueryable` projection mappings.
:::

## Properties / fields

On each mapping method declaration, property and field mappings can be customized.
If a property or field on the target has a different name than on the source, the `MapPropertyAttribute` can be applied.
See also the documentation on [flattening / unflattening](./flattening.md).

```csharp
[Mapper]
public partial class CarMapper
{
    // highlight-start
    [MapProperty(nameof(Car.Model), nameof(CarDto.ModelName))]
    // highlight-end
    public partial CarDto ToDto(Car car);
}
```

### Mapping from source

The source object itself can be mapped to a property of the target object with the `MapPropertyFromSource` attribute:

```csharp
[Mapper]
public partial class CarMapper
{
    // highlight-start
    [MapPropertyFromSource(nameof(CarChanges.OriginalCar))]
    // highlight-end
    public partial CarChanges ToChanges(Car car);
}
```

### Ignore members

To ignore a property or field, the `MapperIgnoreTargetAttribute` or `MapperIgnoreSourceAttribute` can be used.

```csharp
[Mapper]
public partial class CarMapper
{
    // highlight-start
    [MapperIgnoreTarget(nameof(CarDto.MakeId))]
    [MapperIgnoreSource(nameof(Car.Id))]
    // highlight-end
    public partial CarDto ToDto(Car car);
}
```

#### Ignore a member at definition

To ignore a property, field, or method at definition, the `MapperIgnoreAttribute` can be used.

```csharp
public partial class CarMapper
{
    public partial CarDto ToDto(Car car);
}

public class Car
{
    // highlight-start
    [MapperIgnore]
    // highlight-end
    public int Id { get; set; }

    public string ModelName { get; set; }
}

public class CarDto
{
    public string ModelName { get; set; }
}
```

To exclude conversion methods from being used in mappings, annotate them with `MapperIgnore`:

```csharp
public class A
{
    // Prevents using Parse(string) in string -> A mappings
    [MapperIgnore]
    public static A Parse(string s) => new();

    // Alternative factory is still considered
    public static A Create(string s) => new();

    // Prevents using ToB() in A -> B mappings
    [MapperIgnore]
    public B ToB() => new();

    public B Convert() => new();
}
```

If you want to use the `MapperIgnoreAttribute` in another .NET Project than the Mapper itself,
that project needs to preserve the Mapperly attributes at runtime,
see [preserving Mapperly attributes at runtime](../getting-started/installation.mdx#preserving-the-attributes-at-runtime).
Without this, the `MapperIgnoreAttribute` is not visible to Mapperly.

#### Ignore obsolete members

By default, Mapperly will map source/target members marked with `ObsoleteAttribute`.
This can be changed by setting the `IgnoreObsoleteMembersStrategy` of a method with `MapperIgnoreObsoleteMembersAttribute`,
or by setting the `IgnoreObsoleteMembersStrategy` option of the `MapperAttribute`.

| Name   | Description                                                     |
| ------ | --------------------------------------------------------------- |
| None   | Will map members marked with the `Obsolete` attribute (default) |
| Both   | Ignores source and target members with the `Obsolete` attribute |
| Source | Ignores source members with the `Obsolete` attribute            |
| Target | Ignores target members with the `Obsolete` attribute            |

<Tabs>
    <TabItem value="global" label="Global (mapper level)" default>

        Sets the `IgnoreObsoleteMembersStrategy` for all methods within the mapper,
        by default it is `None` allowing obsolete source and target members to be mapped.
        This can be overridden by individual mapping methods using `MapperIgnoreObsoleteMembersAttribute`.

        ```csharp
        // highlight-start
        [Mapper(IgnoreObsoleteMembersStrategy = IgnoreObsoleteMembersStrategy.Both)]
        // highlight-end
        public partial class CarMapper
        {
            ...
        }
        ```

    </TabItem>

    <TabItem value="method" label="Local (mapping method level)">

        Method will use the provided ignore obsolete mapping strategy,
        otherwise the `MapperAttribute` property `IgnoreObsoleteMembersStrategy` will be used.

        ```csharp
        [Mapper]
        public partial class CarMapper
        {
            // highlight-start
            [MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Both)]
            // highlight-end
            public partial CarMakeDto MapMake(CarMake make);
        }
        ```

    </TabItem>

</Tabs>

### Property name mapping strategy

By default, property and field names are matched using a case sensitive strategy.
If all properties/fields differ only in casing, for example `ModelName` on the source
and `modelName` on the target,
the `MapperAttribute` can be used with the `PropertyNameMappingStrategy` option.

```csharp
// highlight-start
[Mapper(PropertyNameMappingStrategy = PropertyNameMappingStrategy.CaseInsensitive)]
// highlight-end
public partial class CarMapper
{
    public partial CarDto ToDto(Car car);
}

public class Car
{
    public string ModelName { get; set; }
}

public class CarDto
{
    public string modelName { get; set; }
}
```

### `null` values

Mapperly allows the customization of how `null` values are handled when mapping properties.

`AllowNullPropertyAssignment` allows to configure whether `null` values are assigned to nullable target properties.
If it is `true` and the source value is `null`, the target property is explicitly set to `null`.
If it is `false`, the property mapping is ignored or an exception is thrown,
depending on the value of `ThrowOnPropertyMappingNullMismatch`.
`AllowNullPropertyAssignment` is `true` by default.

`ThrowOnPropertyMappingNullMismatch` controls how `null` source values are handled,
if the target property is not nullable or `AllowNullPropertyAssignment` is set to `false`.
If `ThrowOnPropertyMappingNullMismatch` is enabled, an `ArgumentNullException` is thrown.
If `ThrowOnPropertyMappingNullMismatch` is disabled, the property mapping is ignored.
`ThrowOnPropertyMappingNullMismatch` is `false` by default.

For mapping methods the behaviour can be specified via `ThrowOnMappingNullMismatch`.
When the mapper tries to return a `null` value from a mapping method with a non-nullable return type,
and `ThrowOnMappingNullMismatch` is set to `false`,
Mapperly tries to return a default value.
For strings the default value is the empty string,
for value types, it is `default`,
for reference types with a public parameterless constructor, a new instance is created.
If no such constructor exists an `ArgumentNullException` is thrown.
If `ThrowOnMappingNullMismatch` is `true` (default), an `ArgumentNullException` is thrown.

:::info
`AllowNullPropertyAssignment`, `ThrowOnPropertyMappingNullMismatch` and `ThrowOnMappingNullMismatch`
are ignored for required init properties and `IQueryable<T>` projection mappings.
:::

### One side strict property mappings

By default Mapperly emits diagnostics with a severity of warning if there are unmapped source or target members.
To enforce strict mappings on only either the source or the target,
the `RequiredMappingStrategy` can be used.

<Tabs>
    <TabItem value="global" label="Global (mapper level)" default>

        Sets the `RequiredMappingStrategy` for all methods within the mapper,
        by default it is `Both` requiring all members to be mapped.
        This can be overridden by individual mapping methods using `MapperRequiredMappingAttribute`.

        ```csharp
        // highlight-start
        [Mapper(RequiredMappingStrategy = RequiredMappingStrategy.Source)]
        // highlight-end
        public partial class CarMapper
        {
            ...
        }
        ```

    </TabItem>

    <TabItem value="local" label="Local (mapping method level)">

        Applied to the specific mapping method.

        ```csharp
        [Mapper]
        public partial class CarMapper
        {
            // highlight-start
            [MapperRequiredMapping(RequiredMappingStrategy.Source)]
            // highlight-end
            public partial CarDto MapCar(Car car);
        }
        ```

    </TabItem>

</Tabs>

### String format

The string format passed to `ToString` calls when converting to a string can be customized
by using the `StringFormat` property of the `MapPropertyAttribute`.

```csharp
[Mapper]
public partial class CarMapper
{
  // highlight-start
  [MapProperty(nameof(Car.Price), nameof(CarDto.Price), StringFormat = "C")]
  // highlight-end
  public partial CarDto MapCar(Car car);
}
```

### String format provider & culture

To customize the format provider / culture to be used by Mapperly when calling `ToString`
format providers can be used.
A format provider can be provided to Mapperly by simply annotating a field or property within the Mapper with the `FormatProviderAttribute`.
The field/property need to return a type implementing `System.IFormatProvider`.
Format providers can be referenced by the name of the property / field in `MapPropertyAttribute.FormatProvider`.
A format provider can be marked as default (set the default property of the `FormatProviderAttribute` to true).
A default format provider is used for all `ToString` conversions
when the source implements has a `ToString` overload accepting a `System.IFormatProvider`.
In a mapper only one format provider can be marked as default.

```csharp
[Mapper]
public partial class CarMapper
{
  // highlight-start
  [FormatProvider(Default = true)]
  private IFormatProvider CurrentCulture => CultureInfo.CurrentCulture;
  // highlight-end

  // highlight-start
  [FormatProvider]
  private readonly IFormatProvider _enCulture = CultureInfo.GetCultureInfo("en-US");
  // highlight-end

  // highlight-start
  [MapProperty(nameof(Car.LocalPrice), nameof(CarDto.LocalPrice), StringFormat = "C")]
  [MapProperty(nameof(Car.ListPrice), nameof(CarDto.ListPrice), StringFormat = "C", FormatProvider = nameof(_enCulture)]
  // highlight-end
  public partial CarDto MapCar(Car car);

  // generates
  target.LocalPrice = source.LocalPrice.ToString("C", CurrentCulture);
  target.ListPrice = source.ListPrice.ToString("C", _enCulture);
}
```

### User-implemented property mappings

To use a custom implemented mapping for a specific property or field,
the `Use` property of the `MapProperty` attribute can be used.
Set it to a unique name of a user-implemented mapping method.
The use of `nameof` is encouraged.
See also [user-implemented mapping methods](./user-implemented-methods.mdx).

<Tabs>
    <TabItem value="auto-user-mappings-enabled" label="enabled AutoUserMappings (default)" default>
        ```csharp
        [Mapper]
        public partial class CarMapper
        {
            // highlight-start
            [MapProperty(nameof(Car.Price), nameof(CarDto.Price), Use = nameof(MapPrice))]
            // highlight-end
            public partial CarDto MapCar(Car source);
    
            // highlight-start
            // set Default = false to not use it for all decimal => string conversions
            // if using AutoUserMappings = false, the UserMapping is not needed.
            [UserMapping(Default = false)]
            private string MapPrice(decimal price)
                => (price / 100).ToString("C");
            // highlight-end
    
            // generates
            target.Price = MapPrice(source.Price);
        }
        ```
    </TabItem>

    <TabItem value="auto-user-mappings-disabled" label="disabled AutoUserMappings">
        ```csharp
        [Mapper(AutoUserMappings = false)]
        public partial class CarMapper
        {
            // highlight-start
            [MapProperty(nameof(Car.Price), nameof(CarDto.Price), Use = nameof(MapPrice))]
            // highlight-end
            public partial CarDto MapCar(Car source);

            // highlight-start
            private string MapPrice(decimal price)
                => (price / 100).ToString("C");
            // highlight-end

            // generates
            target.Price = MapPrice(source.Price);
        }
        ```

    </TabItem>

</Tabs>

#### User-implemented mapping from source

The `Use` property can also be assigned on the `MapPropertyFromSource` attribute, for example, to access multiple properties of the source in the mapping method:

```csharp
[Mapper]
public partial class CarMapper
{
  // highlight-start
  [MapPropertyFromSource(nameof(CarDto.NetPrice), Use = nameof(MapPrice))]
  // highlight-end
  public partial CarDto MapCar(Car source);

  // highlight-start
  private decimal MapPrice(Car car)
    => car.Price - car.Discount;
  // highlight-end

  // generates
  target.NetPrice = MapPrice(source);
}
```

#### Custom mapping names

By default, Mapperly uses the method name as the identifier for each mapping.
However, you can assign a custom name to any mapping by applying the `NamedMapping` attribute:

```csharp
[Mapper]
public partial class CarMapper
{
    // highlight-start
    [MapPropertyFromSource(nameof(CarDto.NetPrice), Use = "CustomMapPrice")]
    // highlight-end
    public partial CarDto MapCar(Car source);

    // highlight-start
    [NamedMapping("CustomMapPrice")]
    // highlight-end
    private decimal MapPrice(Car car)
        => car.Price - car.Discount;
}
```
